﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using mqtt.client.test.Mqtt;
using mqtt.client.test.Mqtt.Connect;
using mqtt.client.test.Mqtt.Subscribe;
using XjjXmm.Infrastructure.Common;
using System.ComponentModel.DataAnnotations;
using mqtt.client.test.Mqtt.Constant;
using System.Threading.Channels;
using mqtt.client.test.Options;
using Amazon.Runtime.Internal.Transform;
using mqtt.client.test.Input;
using mqtt.client.test.Output;

namespace mqtt.client.test;

internal class MqttClientChannel : IDisposable
{
    private Socket socket;

    public Action<Response<string>>? ConnAckAction;

    public Action<Response<string>>? SubAckAction;

    public Action<Response<string>>? PingAckAction;

    public Action<Response<string>>? ReceiveMessageAction;

    public MqttClientChannel(string host, int port)
    {
        IPAddress ip = IPAddress.Parse(host);
        IPEndPoint iPEndPoint = new IPEndPoint(ip, port);
        socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
        //创建与远程主机的连接
        socket.Connect(iPEndPoint);
    }


    public async Task Send(IDataPacket dataPacket)
    {
        await socket.SendAsync(dataPacket.ToBytes());
    }

    private async Task<ArraySegment<byte>> Receive()
    {
        var size = 512;
        var buff = new byte[size];
        ArraySegment<byte> buffer = new ArraySegment<byte>(buff);
        var count = await socket.ReceiveAsync(buffer, SocketFlags.None);
        buffer = buffer[..count];
        
        return await Receive(buffer);
    }
    
    public async Task<ArraySegment<byte>> Receive(ArraySegment<byte> buffer)
    {
       var count =  await socket.ReceiveAsync(buffer, SocketFlags.None);
       
       return buffer[..count];
    }

    public async Task Connect(ConnectInput input)
    {
        await socket.SendAsync(input.ToBytes());
    }

    public Response<string> ReceiveConnack(ArraySegment<byte> buffer)
    {
        var connback = new ConnectOutput(buffer);

        return connback.Result();
    }


    private HashSet<int> publishDict = new HashSet<int>();
    private HashSet<int> publishRelDict = new HashSet<int>();

    public async Task SendPublish0(PublishInput input)
    {
        //var packet = new PublishInput(option);
        await Send(input);
    }

    public async Task SendPublish1(PublishInput input)
    {
       // var packet = new PublishInput(option);

        publishDict.Add(input.PacketIdentifier);

        while (publishDict.Contains(input.PacketIdentifier))
        {
            await socket.SendAsync(input.ToBytes());
            await Task.Delay(5000);
            input.option.Dup = true;
        }
    }

    public async Task SendPublish2(PublishInput input)
    {
       // var packet = new PublishInput(option);

        publishDict.Add(input.PacketIdentifier);

        while (publishDict.Contains(input.PacketIdentifier))
        {
            await socket.SendAsync(input.ToBytes());
            await Task.Delay(5000);
            input.option.Dup = true;
        }


        publishRelDict.Add(input.PacketIdentifier);
        while (publishRelDict.Contains(input.PacketIdentifier))
        {
            await SendPubRel(input.PacketIdentifier);
            await Task.Delay(5000);
        }
    }

 

    public async Task SendPubAck(int packetIdentifier)
    {
        var packet = new PublishAckInput(packetIdentifier);

        await socket.SendAsync(packet.ToBytes());
    }

    public Response<string> ReceivePubAck(ArraySegment<byte> buffer)
    {
        var response = new PublishAckOutput(buffer);

        var res = response.Result().Result;
        publishDict.Remove(res);

        return new Response<string>("");

    }

    public Response<string> ReceivePubRec(ArraySegment<byte> buffer)
    {
        var response = new PublishRecOutput(buffer);

        var res = response.Result().Result;
        publishDict.Remove(res);

        return new Response<string>("");

    }

    public async Task SendPubRel(int packetIdentifier)
    {
        var packet = new PublishRecInput(packetIdentifier);

        await socket.SendAsync(packet.ToBytes());
    }
    
    public Response<int> ReceivePubRel(ArraySegment<byte> buffer)
    {
        var response = new PublishRelOutput(buffer);

        return response.Result();
    }

    public Response<string> ReceivePubComp(ArraySegment<byte> buffer)
    {
        var response = new PublishCompOutput(buffer);

        var res = response.Result().Result;
        publishRelDict.Remove(res);

        return new Response<string>("");

    }

    public async Task SendSubscribe(SubscribeInput input)
    {
       // var packet = new SubscribeDataPacket(option);

        await socket.SendAsync(input.ToBytes());
    }

    public Response<string> ReceiveSuback(ArraySegment<byte> buffer)
    {
        
        
        var connback = new SubscribeOutput(buffer);

        return connback.Result();
    }
    
    public async Task SendPingReq()
    {
        var packet = new PingDataInput();

         await socket.SendAsync(packet.ToBytes());
       
    }
    
    public Response<string> ReceivePingResp(ArraySegment<byte> buffer)
    {
        return new Response<string>("");
    }
    
    /*
    private async Task ReceiveMessage2(ArraySegment<byte> buffer)
    {
        var receiveData = new List<byte>();

        var bytes = buffer[1..];

        int len = 0;
        while (true)
        {
            if (receiveData.Count == 0)
            {
                len = Util.ComputeRemainingLength(bytes);
                if (len < 128)
                {
                    bytes = bytes[1..];
                }
                else if (len < 16384)
                {
                    bytes = bytes[2..];
                }
                else
                {
                    bytes = bytes[3..];
                }

                var subjectLength = (bytes[0] << 8) | bytes[1];
                len = len - subjectLength - 2;


                bytes = bytes.Slice(subjectLength + 2);
            }


            receiveData.AddRange(bytes.ToArray());

            if (receiveData.Count == len)
            {
                Console.WriteLine(receiveData.ToStrings());
                Console.WriteLine();

                //  string test = "乐山大佛你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗结尾乐山大佛你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗结尾乐山大佛你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗结尾";
                // var testByte = GetBytes(test);

                receiveData.Clear();
                break;
            }
            else
            {
                var count = await socket.ReceiveAsync(buffer, SocketFlags.None);
                bytes = buffer[0..count];
            }
        }
    }
    */


    //private async Task<Response<string>> ReceiveMessage(ArraySegment<byte> buffer, byte qos)
    //{
    //    ReceiveResponse receiveResponse = new ReceiveResponse(buffer, socket, qos);

    //    return await receiveResponse.Result();
    //}

    /*public async Task<ArraySegment<byte>> Receive()
    {
        var size = 512;
        var buff = new byte[size];
        ArraySegment<byte> buffer = new ArraySegment<byte>(buff);

        var count = await socket.ReceiveAsync(buffer, SocketFlags.None);

        return buffer[..count];
    }*/

  /*  public async Task Receive()
    {
        var size = 512;
        var buff = new byte[size];
        ArraySegment<byte> buffer = new ArraySegment<byte>(buff);

        while (true)
        {
            var count = await socket.ReceiveAsync(buffer, SocketFlags.None);
            if (count == 0)
            {
                continue;
            }

            var bit = buffer[0];
            var data = buffer[..count];
            switch (bit)
            {        
                case (PacketType.CONNACK << 4) | HeaderFlag.CONNACK:
                    ConnAckAction?.Invoke(await ReceiveConnack(data));
                    break;
                case (PacketType.SUBACK << 4) | HeaderFlag.SUBACK:
                    SubAckAction?.Invoke(await ReceiveSuback(data));
                    break;
                case PacketType.PUBLISH << 4:
                    ReceiveMessageAction?.Invoke(await ReceiveMessage(data, Qos.Qos0));

                    break;
                case PacketType.PUBLISH << 4 | Qos.Qos1 << 1:
                    ReceiveMessageAction?.Invoke(await ReceiveMessage(data, Qos.Qos1));

                    break;
                case PacketType.PUBLISH << 4 | Qos.Qos1 << 1 | 1 << 3:
                    ReceiveMessageAction?.Invoke(await ReceiveMessage(data, Qos.Qos1));

                    break;

                case PacketType.PUBLISH << 4 | Qos.Qos2 << 1:
                    ReceiveMessageAction?.Invoke(await ReceiveMessage(data, Qos.Qos2));

                    break;
                case PacketType.PUBLISH << 4 | Qos.Qos2 << 1 | 1 << 3:
                    ReceiveMessageAction?.Invoke(await ReceiveMessage(data, Qos.Qos2));

                    break;

                case (PacketType.PINGRESP << 4) | HeaderFlag.PINGRESP:
                    PingAckAction?.Invoke(ReceivePingResp(data));
                    break;

                case (PacketType.PUBACK << 4) | HeaderFlag.PUBACK:
                    await ReceivePubAck(data);
                    break;

                case (PacketType.PUBREC << 4) | HeaderFlag.PUBREC:
                    await ReceivePubRec(data);
                    break;

                case (PacketType.PUBCOMP << 4) | HeaderFlag.PUBCOMP:
                    await ReceivePubComp(data);
                    break;
            }
        }
    }
*/

    public async Task<dynamic> ReceiveMessage(ArraySegment<byte> buffer)
    {
        var receiveData = new List<byte>();

        var qos = (byte)(buffer[0] & 0x06) >> 1;

        var bytes = buffer[1..];

        int len = 0;

        int packetIdentifier = 0;

        while (true)
        {
            if (receiveData.Count == 0)
            {
                len = Util.ComputeRemainingLength(bytes);
                if (len < 128)
                {
                    bytes = bytes[1..];
                }
                else if (len < 16384)
                {
                    bytes = bytes[2..];
                }
                else
                {
                    bytes = bytes[3..];
                }

                var subjectLength = (bytes[0] << 8) | bytes[1];
                len = len - subjectLength - 2;

                bytes = bytes[(subjectLength + 2)..];

                if (qos > 0)
                {
                    len -= 2;
                    packetIdentifier = bytes[0] << 8 | bytes[1];

                    bytes = bytes[2..];
                }


                if (len < bytes.Count)
                {
                    bytes = bytes[0..len];
                }
            }


            receiveData.AddRange(bytes.ToArray());

            if (receiveData.Count == len)
            {
                //Console.WriteLine(receiveData.ToStrings());
                //Console.WriteLine();

                //  string test = "乐山大佛你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗结尾乐山大佛你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗结尾乐山大佛你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗结尾";
                // var testByte = GetBytes(test);

                //receiveData.Clear();

                //if (qos == Qos.Qos1)
                //{
                //    await SendPubAck(packetIdentifier);
                //}
                //else if (qos == Qos.Qos2)
                //{
                //    await SendPubRec(packetIdentifier);

                //    var count = await socket.ReceiveAsync(buffer, SocketFlags.None);
                //    bytes = buffer[0..count];

                //    var res = new PublishRelOutput(bytes);
                //    await SendPubComp(res.Result().Result);
                //}

                return new
                {
                    msg = receiveData.ToStrings(),
                    packetIdentifier
                };
            }
            else
            {
                var count = await socket.ReceiveAsync(buffer, SocketFlags.None);
                bytes = buffer[0..count];
            }
        }
    }



    internal async Task SendPubRec(int packetIdentifier)
    {
        var packet = new PublishRecInput(packetIdentifier);

        await socket.SendAsync(packet.ToBytes());
    }

    internal async Task SendPubComp(int packetIdentifier)
    {
        var packet = new PublishCompleteInput(packetIdentifier);

        await socket.SendAsync(packet.ToBytes());


    }

    public void Dispose()
    {
        socket.Close();
    }
}